<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
    <link href="/assets/bootstrap/css/bootstrap.min.css" rel="stylesheet">
    <link href="/assets/bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
    <link href="/assets/bootstrap/css/style.min.css" rel="stylesheet">
</head>

<body>
<div class="container-fluid p-t-15">
    <div class="row">

        <div class="col-lg-12">
            <div class="card">
                <header class="card-header">
                    <div class="card-title">如何给调用方开通 KEY 和 SECRET？</div>
                </header>

                <div class="card-body">
                    <p>1. 新增调用方，输入调用方标识、调用方对接人、备注等信息；</p>
                    <p>2. 授权调用方可调用的接口；</p>
                    <p>3. 查看详情，将调用方的 <code>KEY</code>、<code>SECRET</code> 发给调用方；</p>
                </div>
            </div>
        </div>

        <div class="col-lg-12">
            <div class="card">
                <header class="card-header">
                    <div class="card-title">调用方如何传递 Token？</div>
                </header>

                <div class="card-body">
                    <p>基于 HTTP Header 中的两个参数 <code>Authorization</code>、<code>Authorization-Date</code> 存储签名信息。</p>
                    <p>1. Authorization 存储签名信息，格式：调用方 KEY + 空格分隔符 + 摘要(加密串)，例如：</p>
                    <pre>Authorization:blog MjJjMDE1MWFkZjMwOWFmYjFlNzViNDFjYjYwMWFlMmM=</pre>
                    <p>2. Authorization-Date 存储时间信息，格式：0000-00-00 00:00:00，使用 <code>Asia/Shanghai</code> 时区，例如；</p>
                    <pre>Authorization-Date:2021-04-03 21:12:36</pre>
                </div>
            </div>
        </div>

        <div class="col-lg-12">
            <div class="card">
                <header class="card-header"><div class="card-title">不同语言生成签名的方法，供参考</div></header>
                <div class="card-body">

                    <ul class="nav nav-tabs">
                        <li class="nav-item">
                            <a class="nav-link active" data-toggle="tab" href="#go" aria-selected="true">Go 语言</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link" data-toggle="tab" href="#php" aria-selected="false">PHP 语言</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link" data-toggle="tab" href="#js" aria-selected="false">JS 语言</a>
                        </li>
                    </ul>

                    <div class="tab-content">
                        <div class="tab-pane fade active show" id="go">
                            <pre>
func New(key, secret string, ttl time.Duration) Signature {
	return &signature{
		key:    key,
		secret: secret,
		ttl:    ttl,
	}
}

// Generate
// path 请求的路径 (不附带 querystring)
func (s *signature) Generate(path string, method string, params url.Values) (authorization, date string, err error) {
	if path == "" {
		err = errors.New("path required")
		return
	}

	if method == "" {
		err = errors.New("method required")
		return
	}

	methodName := strings.ToUpper(method)
	if !methods[methodName] {
		err = errors.New("method param error")
		return
	}

	// Date
	date = time_parse.CSTLayoutString()

	// Encode() 方法中自带 sorted by key
	sortParamsEncode, err := url.QueryUnescape(params.Encode())
	if err != nil {
		err = errors.Errorf("url QueryUnescape %v", err)
		return
	}

	// 加密字符串规则
	buffer := bytes.NewBuffer(nil)
	buffer.WriteString(path)
	buffer.WriteString(delimiter)
	buffer.WriteString(methodName)
	buffer.WriteString(delimiter)
	buffer.WriteString(sortParamsEncode)
	buffer.WriteString(delimiter)
	buffer.WriteString(date)

	// 对数据进行 sha256 加密，并进行 base64 encode
	hash := hmac.New(sha256.New, []byte(s.secret))
	hash.Write(buffer.Bytes())
	digest := base64.StdEncoding.EncodeToString(hash.Sum(nil))

	authorization = fmt.Sprintf("%s %s", s.key, digest)
	return
}

// 模拟数据
const (
	key    = "blog"
	secret = "i1ydX9RtHyuJTrw7frcu"
	ttl    = time.Minute * 10
)

func TestSignature_Generate(t *testing.T) {
	path := "/echo"
	method := "POST"

	params := url.Values{}
	params.Add("a", "a1")
	params.Add("d", "d1")
	params.Add("c", "c1 c2*")

	authorization, date, err := New(key, secret, ttl).Generate(path, method, params)
	t.Log("authorization:", authorization)
	t.Log("authorization-date:", date)
	t.Log("err:", err)
}
                            </pre>
                        </div>
                        <div class="tab-pane fade" id="php">
                            <pre>
// 模拟数据
$key    = "blog";
$secret = "i1ydX9RtHyuJTrw7frcu";

$path = "/echo";
$method = "POST";

$params['a'] = "a1";
$params['d'] = "d1";
$params['c'] = "c1 c2*";

// 对 params key 进行排序
ksort($params);

// 对 sortParams 进行操作
$sortParamsEncode = rawurldecode(http_build_query($params, "", "&", PHP_QUERY_RFC3986));

// 时间 使用 Asia/Shanghai 时区
$date = date("Y-m-d H:i:s", time());

// 加密字符串规则
$encryptStr = $path."|".strtoupper($method)."|".$sortParamsEncode."|".$date;

// 对数据进行 sha256 加密，并进行 base64 encode
$digest = base64_encode(hash_hmac("sha256", $encryptStr, $secret, true));

$authorization = $key." ".$digest;

echo "authorization:{$authorization}";
echo "---";
echo "authorization-date:{$date}";
                            </pre>
                        </div>
                        <div class="tab-pane fade" id="js">
                            <pre>
let key = "blog";
let secret = "i1ydX9RtHyuJTrw7frcu";

let date = new Date();
let datetime = date.getFullYear() + "-" // "年"
    + ((date.getMonth() + 1) > 10 ? (date.getMonth() + 1) : "0" + (date.getMonth() + 1)) + "-" // "月"
    + (date.getDate() < 10 ? "0" + date.getDate() : date.getDate()) + " " // "日"
    + (date.getHours() < 10 ? "0" + date.getHours() : date.getHours()) + ":" // "小时"
    + (date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes()) + ":" // "分钟"
    + (date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds()); // "秒"

let path = "/echo";
let method = "POST";
let params = {a:'a1', d:'d1', c: 'c1 c2*'};

let sortParamsEncode = decodeURIComponent(jQuery.param(ksort(params)));
let encryptStr = path + "|" + method.toUpperCase() + "|" + sortParamsEncode + "|" + datetime;
let digest = CryptoJS.enc.Base64.stringify(CryptoJS.HmacSHA256(encryptStr, secret));
console.log({authorization: key + " " + digest, date: datetime});
                            </pre>
                        </div>
                    </div>

                </div>
            </div>
        </div>
    </div>
</div>
<script type="text/javascript" src="/assets/bootstrap/js/jquery.min.js"></script>
<script type="text/javascript" src="/assets/bootstrap/js/popper.min.js"></script>
<script type="text/javascript" src="/assets/bootstrap/js/bootstrap.min.js"></script>
<script type="text/javascript" src="/assets/bootstrap/js/main.min.js"></script>
<script type="text/javascript">document.write(unescape("%3Cspan id='cnzz_stat_icon_1279911342'%3E%3C/span%3E%3Cscript src='https://v1.cnzz.com/z_stat.php%3Fid%3D1279911342%26' type='text/javascript'%3E%3C/script%3E"));</script>
</body>
</html>
